package com.lz.sports.vm

import android.hardware.usb.UsbDevice
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import com.lz.comm.SP
import com.lz.library.base.BaseModel
import com.lz.library.base.BaseViewModel
import com.lz.library.extension.APP
import com.lz.library.extension.countDown
import com.lz.library.extension.resString
import com.lz.library.extension.showToast
import com.lz.sports.LogUtils
import com.lz.sports.R
import com.lz.sports.bean.DeviceGroup
import com.lz.sports.bean.DeviceInfo
import com.lz.sports.bean.DeviceInfoTag
import com.lz.sports.bean.ExamInspectGroup
import com.lz.sports.core.*
import com.lz.sports.db.DeviceDatabase
import com.lz.sports.db.ExamineeDatabase
import com.lz.sports.utils.ToastUtils
import com.lz.sports.utils.hexCheckCode
import com.lz.ub.UBCCallback
import com.lz.ub.UBCConfig
import com.lz.ub.UBCDevice
import com.lz.ub.UBCManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import java.util.*

//开始考试
class ExamViewModel : BaseViewModel<BaseModel>(), UBCCallback, UBCConfig.UsbStateObserver {

    private val examPlanId = ProjectManager.examPlanId()
    //项目ID
    private val projectId = ProjectManager.examProjectId()
    //考区ID
    private val examAreaId = ProjectManager.examAreaId()
    //考点ID
    private val examSiteId = ProjectManager.examSiteId()

    companion object {

        //准备时间
        const val BEGIN_TIME = 5

        //时间格式化
        private const val TIME_FORMAT = "%02d : %02d"

        //结束时的延时弹窗
        private const val END_DELAY = 1000L
    }

    //检录组
    val inspectGroupList = ArrayList<ExamInspectGroup>()
    //当前选择
    private var selectPosition = -1
    //上一次考试的组
    private var lastGroup: ExamInspectGroup? = null
    //当前考试组
    var currentGroup: ExamInspectGroup? = null

    //剩余
    val leftDesc = MutableLiveData<CharSequence>()

    //当前设备组
    var deviceGroup: DeviceGroup? = null

    //组
    val groupSelect = MutableLiveData(APP.resources.getString(R.string.select))

    //最大考试组
    private val maxGroupSize = SP.getInt(K_RULE_EXAM_GROUP_COUNT, v_ruleExamGroupCount)

    //notify adapter
    var refreshAdapter = MutableLiveData(false)

    //倒计时间
    val downTimeText = MutableLiveData<String>()

    //正在考试
    private var isExamining = false

    //各种状态
    val shouldSelect = MutableLiveData(false)
    val shouldReExam = MutableLiveData(false)
    val shouldPre = MutableLiveData(false)
    val shouldStart = MutableLiveData(false)
    val shouldNext = MutableLiveData(false)
    val shouldSave = MutableLiveData(false)
    val shouldPrint = MutableLiveData(false)

    //开始与结束自动播放声音
    private var soundStart: Boolean = false
    private var soundEnd: Boolean = false
    private val soundHelper by lazy { SoundHelper() }
    //自动保存成绩
    var autoSave = MutableLiveData(false)

    //考试时间
    private var scoreTime = v_ruleScoreTime
    //倒计时模式
    private var modelCommand = COUNTDOWN

    override fun onCreate(owner: LifecycleOwner) {
        soundHelper.init(intArrayOf(R.raw.start, R.raw.end, R.raw.prepare, R.raw.result))
        val soundAuto = SP.getBoolean(K_SOUND_AUTO, v_sound_auto)
        soundStart = soundAuto && SP.getBoolean(K_SOUND_START, v_sound_start)
        soundEnd = soundAuto && SP.getBoolean(K_SOUND_END, v_sound_end)
        scoreTime = SP.getInt(K_RULE_SCORE_TIME, v_ruleScoreTime)
        autoSave.value = SP.getBoolean(K_RULE_EXAM_SAVE, v_ruleExamSave)
        if (scoreTime < v_ruleScoreTime / 10) scoreTime = v_ruleScoreTime / 10

        val hexTime = String.format("%04X", scoreTime)
        modelCommand = hexCheckCode("$DOWNTIME$hexTime")
        LogUtils.i("modelCommand = $modelCommand")

        UBCManager.get().addUBCCallback(this)
        UBCManager.get().restart()
        UBCConfig.observe(owner, this)
        launch {
            flow { emit(ExamineeDatabase.get().inspectGroupList(examPlanId, projectId, examAreaId, examSiteId)) }
                .flowOn(Dispatchers.IO)
                .onStart {
                    updateDialogLoading(true)
                }.onCompletion {
                    updateDialogLoading(false)
                }.collect {
                    updateDialogLoading(false)
                    inspectGroupList.addAll(it)
                    left()
                    refreshState()
                }
        }
    }

    override fun onCleared() {
        super.onCleared()
        soundHelper.release()
        UBCManager.get().removeUBCCallback(this)
        UBCManager.get().cancelAndClearAuto()
    }

    private fun left() {
        var count = 0
        var groupCount = 0
        inspectGroupList.forEach {
            if (!it.hasExamined()) {
                count += it.inspectList.size
                groupCount++
            }
        }
        val spanStr = SpannableStringBuilder(R.string.exam_left0.resString())
        var start = spanStr.length
        spanStr.append("$groupCount")
        spanStr.setSpan(
            ForegroundColorSpan(APP.getColor(R.color.color_ff8b26)),
            start, spanStr.length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
        spanStr.append(R.string.exam_left1.resString())
        start = spanStr.length
        spanStr.append("$count")
        spanStr.setSpan(
            ForegroundColorSpan(APP.getColor(R.color.color_ff8b26)),
            start, spanStr.length, Spannable.SPAN_INCLUSIVE_EXCLUSIVE
        )
        spanStr.append(R.string.exam_left2.resString())
        leftDesc.value = spanStr
    }

    //选择检录组
    fun selectInspectGroup(position: Int) {
        if (position == selectPosition) return
        selectPosition = position
        currentGroup = inspectGroupList[selectPosition]
        groupSelect.value = currentGroup!!.str()
        val needReset = lastGroup != null && !lastGroup!!.isSameDeviceGroup(currentGroup!!)
        val needConnect = lastGroup == null || needReset
        if (needConnect) {
            //后面按序号排序好设备列表
            deviceGroup = DeviceDatabase.get().findDeviceGroupById(currentGroup!!.deviceGroupId)
        }
        refreshState()
        if (deviceGroup == null) {
            ToastUtils.showMyToast(R.string.exam_group_un)
        } else {
            launch {
                updateDialogLoading(true)
                bindDevice(currentGroup!!, deviceGroup!!, needReset, needConnect)
                updateDialogLoading(false)
            }
        }
        lastGroup = currentGroup
    }

    //检录组绑定设备
    private suspend fun bindDevice(inspectGroup: ExamInspectGroup, deviceGroup: DeviceGroup, reset: Boolean, needConnect: Boolean) {
        LogUtils.i("bindDevice $reset, $needConnect")
        if (reset) UBCManager.get().cancelAndClearAuto()
        withContext(Dispatchers.Default) {
            if (needConnect) {
                inspectGroup.inspectList.forEach { it.reset() }
                var index = 0
                val deviceList = LinkedList<UBCDevice>()
                while (index < deviceGroup.deviceList().size && index < maxGroupSize) {
                    val deviceInfo = deviceGroup.deviceList()[index]
                    val tag = DeviceInfoTag(deviceGroup.id, deviceInfo.id)
//                    UBCManager.get().addDevice(deviceInfo.mac, 11, 10, true, tag)
                    deviceList.add(UBCDevice(deviceInfo.mac, 11, 10, true, tag))
                    index++
                }
                UBCManager.get().addDevice(deviceList)
            } else {
                inspectGroup.inspectList.forEach { inspect ->
                    deviceGroup.findDeviceInfo(inspect.number).also {
                        if (it != null) inspect.reset(it) else inspect.reset()
                    }
                }
            }
        }
        refreshAdapter.value = true
        if (reset) delay(UBCConfig.RESTART_TIME)
        UBCManager.get().connect()
    }

    //刷新各状态
    private fun refreshState() {
        shouldSelect.value = !isExamining && inspectGroupList.isNotEmpty()

        shouldReExam.value = !isExamining && deviceGroup != null && currentGroup?.shouldReExaminee() == true
        shouldPre.value = !isExamining && selectPosition > 0
        shouldNext.value = !isExamining && selectPosition >= 0 && selectPosition < inspectGroupList.size - 1

        shouldStart.value = !isExamining && deviceGroup != null && currentGroup?.shouldExaminee() == true
        shouldSave.value = !isExamining && deviceGroup != null && currentGroup?.shouldReExaminee() == true
        shouldPrint.value = shouldSave.value
    }

    fun canBack(): Boolean = !isExamining

    fun pre() {
        selectInspectGroup(selectPosition - 1)
    }

    //预备开始
    fun preStart() {
        UBCManager.get().pushHexCommand(modelCommand)
        launch {
            delay(1500)
            countDown(BEGIN_TIME - 3).collect { checkInitModels() }
        }
    }

    //重新考试预备
    fun restartPre() {
        currentGroup?.inspectList?.forEach { it.repeat() }
        UBCManager.get().pushHexCommand(modelCommand)
        launch {
            delay(1500)
            countDown(BEGIN_TIME - 3).collect { checkInitModels() }
        }
    }

    //检查未初始化模式的项
    private fun checkInitModels() {
        val current = currentGroup ?:return
        val macs = current.inspectList.mapNotNull {
            if (!it.hasInitModel) {
                deviceGroup?.findDeviceInfo(it.number)?.mac
            } else {
                null
            }
        }
        UBCManager.get().pushHexCommand(macs, modelCommand)
    }

    //倒计时
    private var downTimeJob: Job? = null

    fun start() {
        if (soundStart) soundHelper.play(0)
        launch {
            downTimeJob?.cancel()
            downTimeJob = launch {
                countDown(scoreTime).onStart {
                    isExamining = true
                    refreshState()
                    downTimeText.value = String.format(TIME_FORMAT, scoreTime / 60, scoreTime % 60)
                }.onCompletion {
//                    isExamining = false
                    downTimeText.value = null
                    refreshState()
                }.collect {
                    if (it == 0) { //结束
                        if (soundEnd) soundHelper.play(1)
//                        UBCManager.get().pushHexCommand(PAUSE)
                        updateDialogLoading(true)
                        delay(END_DELAY)
                        if (autoSave.value!!) saveResult()
                        currentGroup?.hasTmpExamined = true
                        isExamining = false
                        refreshState()
                        left()
                        updateDialogLoading(false)
                    } else {
                        downTimeText.value = String.format(TIME_FORMAT, it / 60, it % 60)
                    }
                    UBCManager.get().pushHexCommand(ONCE_DATA)
                }
            }
        }
    }

    fun reset() {
        UBCManager.get().restart()
        launch {
            updateDialogLoading(true)
            delay(2000)
            if (deviceGroup != null && currentGroup != null) {
                bindDevice(currentGroup!!, deviceGroup!!, false, true)
            }
            updateDialogLoading(false)
        }
    }

    fun isAllDeviceConnected(): Boolean {
        if (currentGroup != null) {
            currentGroup!!.inspectList.forEach {
                if (!it.isOnLine()) return false
            }
            return true
        }
        return false
    }

    fun next() {
        selectInspectGroup(selectPosition + 1)
    }

    //保存成绩
    fun save() {
        launch {
            saveResult()
            R.string.exam_save_s.showToast()
            refreshState()
            left()
        }
    }

    private suspend fun saveResult() {
        val currentGroup = this.currentGroup ?: return
        currentGroup.saveExamineeResult()
        withContext(Dispatchers.Default) {
            ExamineeDatabase.get().saveExamineeResult(currentGroup)
        }
        groupSelect.postValue(currentGroup.str())
    }

    fun playSound(index: Int) {
        soundHelper.play(index)
    }

    fun printer() {
        "该功能暂未开放".showToast()
    }

    override fun onConnected(device: UBCDevice, isAssist: Boolean) {
        if (!isAssist) changeLineState(device, true)
    }

    override fun onDisconnected(device: UBCDevice, isAssist: Boolean) {
        if (!isAssist) changeLineState(device, false)
    }

    private fun changeLineState(device: UBCDevice, onLine: Boolean) {
//        LogUtils.i("changeLineState $onLine, ${device.mac}")
        val tag = device.tag ?: return
        if (tag !is DeviceInfoTag || tag.deviceGroupId != deviceGroup?.id) return
        val deviceInfo = deviceGroup?.findDeviceInfo(device.mac) ?: return
        deviceInfo.changeLine(onLine)
        if (tag.deviceGroupId != currentGroup?.deviceGroupId) return
        currentGroup?.findInspectExamineeByNumber(deviceInfo.number)?.changeState(onLine)
    }

    override fun onReadHex(device: UBCDevice, bytes: ByteArray, start: Int, size: Int, isAssist: Boolean) {
//        LogUtils.i("onReadHexPre... ${device.mac}, $start, $size")
        if (isAssist) return
        val tag = device.tag ?: return
        if (tag !is DeviceInfoTag || tag.deviceGroupId != currentGroup?.deviceGroupId
            || tag.deviceGroupId != deviceGroup?.id) return
        val deviceInfo = deviceGroup?.findDeviceInfo(device.mac) ?: return
        LogUtils.i("onReadHex ${deviceInfo.number}, $start, $size")
        if (bytes.getOrNull(start) == HEADER) refreshData(deviceInfo, bytes, start, size)
    }

    //刷新当前数据
    private fun refreshData(deviceInfo: DeviceInfo, bytes: ByteArray, start: Int, size: Int) {
        //从这里回调的设置肯定就是连接状态的
        when (bytes[start + 1].toUByte().toInt().shl(8) + bytes[start + 2]) {
            START -> {
            }
            MODEL -> {
                LogUtils.i("refresh model ${deviceInfo.number} success")
                currentGroup?.findInspectExamineeByNumber(deviceInfo.number)?.initModel()
            }
            DATA -> {
                val battery = bytes[start + 3].toUByte().toInt()
                val num = bytes[start + 4].toUByte().toInt().shl(8) + bytes[start + 5].toUByte().toInt()
                val mixing = bytes[start + 12].toUByte().toInt()
                LogUtils.i("refreshData $num, $battery, $mixing, ${deviceInfo.number}")
                val examinee = currentGroup?.findInspectExamineeByNumber(deviceInfo.number) ?: return
                if (isExamining) {
                    examinee.changeData(num, battery, mixing)
                } else {
                    examinee.changeBattery(battery)
                }
            }
            BUZZING -> {
                //暂不处理
                //从这里回调的设置肯定就是连接状态的
            }
        }
    }

    override fun onRead(device: UBCDevice, data: String, isAssist: Boolean) = Unit

    override fun onStateChanged(usbDevice: UsbDevice, state: Int) {
        if (state == UBCConfig.DEVICE_DETACHED) {
            ToastUtils.showMyToast(R.string.exam_device_out)
            downTimeJob?.cancel()
            isExamining = false
            downTimeText.value = null
            refreshState()
            currentGroup?.inspectList?.forEach { it.repeat(); it.changeState(false) }
        }
    }
}